package www.lagou.dao;

import org.apache.commons.dbutils.DbUtils;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import www.lagou.entity.Account;
import www.lagou.utils.DateUtils;
import www.lagou.utils.DruidUtils;

import java.sql.Connection;
import java.sql.SQLException;

/**
 * 处理转账业务。
 */
public class AccountTransfer {
    // 用到的 SQL 语句。
    private static final String SQL_GET_ACCOUNT_BY_ID = "SELECT id, username, card, balance FROM account WHERE id = ?";
    private static final String SQL_SET_BALANCE = "UPDATE account SET balance = ? WHERE id = ?";
    private static final String SQL_INSERT_TX = "INSERT INTO `transaction`(cardid, tratype, tramoney, tradate) VALUES (?, ?, ?, ?)";

    /**
     * 通过账户 ID 进行账户间转账，并记录交易信息。
     *
     * @param fromId 转账方 ID。
     * @param toId 入账方 ID。
     * @param moneyAmount 转账金额，必须大于等于零。
     * @throws SQLException 数据库执行出错。
     * @throws AccountTransferException 转账金额不足，或转账账户不存在。
     * @throws IllegalArgumentException 转账金额不是大于等于零且不为正无穷的浮点数。
     */
    public static void transferBalance(int fromId, int toId, double moneyAmount) throws SQLException, AccountTransferException {
        if (!Double.isFinite(moneyAmount) || moneyAmount < 0.0) {
            throw new IllegalArgumentException("invalid money amount");
        }

        Connection conn = null;
        try {
            // 获取链接，开启事务。
            conn = DruidUtils.getConnection();
            conn.setAutoCommit(false);

            // 查询账户。
            QueryRunner runner = new QueryRunner();
            Account from = runner.query(conn, SQL_GET_ACCOUNT_BY_ID, new BeanHandler<>(Account.class), fromId);
            Account to = runner.query(conn, SQL_GET_ACCOUNT_BY_ID, new BeanHandler<>(Account.class), toId);
            if (from == null || to == null) {
                throw new AccountTransferException("account id not exists");
            }

            // 判断余额，设置转账。
            if (from.getBalance() < moneyAmount) {
                throw new AccountTransferException("account short balance");
            }
            from.setBalance(from.getBalance() - moneyAmount);
            to.setBalance(to.getBalance() + moneyAmount);

            // 写入账户更新。
            runner.update(conn, SQL_SET_BALANCE, from.getBalance(), from.getId());
            runner.update(conn, SQL_SET_BALANCE, to.getBalance(), to.getId());

            // 记录交易日志。
            String now = DateUtils.now();
            runner.update(conn, SQL_INSERT_TX, from.getCard(), "转出", moneyAmount, now);
            runner.update(conn, SQL_INSERT_TX, to.getCard(), "转入", moneyAmount, now);

            // 提交事务。
            conn.commit();
        } catch (Exception e) {
            // 失败回滚。
            DbUtils.rollback(conn);
            throw e;
        } finally {
            // 归还连接。
            DbUtils.close(conn);
        }
    }
}
